# 一种中文编程语言的语法设计

本文说明了一种中文编程语言的语法设计。
此编程语言基于现代汉语, 简体中文, 语法设计比较接近中文的习惯,
比如无空格语法, 主谓宾语法等。
此编程语言整体设计比较简单, 可用于编程入门教学,
也可嵌入更大的软件作为插件/扩展的脚本语言。
此编程语言在设计上参考了多种主流的编程语言,
可以编译成 WebAssembly (wasm), 从而在广泛的环境/设备上运行。

关键词: 计算机, 编程语言, 中文编程, 语法设计

本文的重点不在 "是什么", 而在 "为什么"。
关于此编程语言的完整的具体的语法设计, 请见其参考手册。
本文主要说明为什么这么设计。


## 目录

+ 1 编程语言简介

+ 2 主要设计目标

  - 2.1 主要语言特性

  - 2.2 预期的使用者

  - 2.3 主要用途

  - 2.4 后端

  - 2.5 主要设计原则

+ 3 参考 python

  - 3.1 注释和缩进

  - 3.2 定义

+ 4 参考 rust

  - 4.1 内置数据类型

  - 4.2 自动类型推导

  - 4.3 表达式和语句

  - 4.4 没有类和继承

  - 4.5 枚举和模式匹配

+ 5 参考月兔 (moonbit)

  - 5.1 方法函数

  - 5.2 字符串插值

+ 6 参考 js (JavaScript)

  - 6.1 模块与导入导出

  - 6.2 顶级语句

  - 6.3 闭包

  - 6.4 自动数据类型

+ 7 中文语法

  - 7.1 无空格语法 (自动分词)

  - 7.2 主谓宾语法

  - 7.3 歧义处理 (分词歧义)

+ 8 总结

  - 8.1 对编程语言的主观偏见


## 1 编程语言简介

"无歧义中文编程语言" 简称 "歧语言" (nb_lang)。

代码像这样:

+ `第一个程序.歧`

  ```
  输出 666。
  ```

+ `循环.歧`

  ```
  # 这是注释

  循环 3 次:
    写 6。

  换行。
  ```

+ `吃货.歧`

  ```
  定义类型吃货:
    名字(文本)。

  定义吃, 参数自己(吃货)、东西(文本):
    输出 "\(自己的名字)吃了\(东西)"。

  # 测试
  定义变量窝, 吃货。  # 窝是吃货 ^_^
  窝的名字是 "喵喵"。

  定义变量饭, "汤面条"。

  窝吃饭。  # 喵喵吃了汤面条
  ```

此项目还处于早期阶段。
(TODO) 歧语言编译器目前只能运行一些简单的示例代码。
(相关链接可能放在评论区)

歧语言的设计参考了多种现有的 (成熟) 编程语言:

+ [python](https://www.python.org/)
+ [月兔 (moonbit)](https://www.moonbitlang.cn/)
+ [rust](https://www.rust-lang.org/)
+ js (JavaScript)


## 2 主要设计目标

本节说明对歧语言设计的整体规划。

### 2.1 主要语言特性

+ 基于中文的语法设计:

  现代汉语, 简体中文 (`zh_CN`)

  尽量接近现代汉语的使用习惯, 降低阅读理解的难度。

+ 静态类型:

  每个变量都具有确定的数据类型, 有时候需要手动标记数据类型。
  也有一部分自动类型推导。

  在编译时进行数据类型检查, 避免数据类型错误。

+ 自动内存管理:

  具有垃圾收集 (GC), 程序员无需 (也不能) 手动分配内存。

### 2.2 预期的使用者

歧语言在设计上的目标使用者, 是所有初中毕业的中国人 (成年人)。
也就是已经完成九年义务教育。

所以在设计时, 必须考虑主要目标使用者的知识水平。

窝觉得编程是个好东西, 所有使用计算机的人, 学点编程都是有好处的。

### 2.3 主要用途

歧语言预计具有这些主要用途:

+ **入门级教学语言**:

  没有基础的编程语言初学者, 可以通过学习歧语言来入门编程。
  也可用于科普, 比如介绍和演示编程语言的工作原理。

  歧语言整体设计比较简单, 没有太多的语言功能特性,
  并且对英语水平没有要求, 可以降低编程入门的学习难度。

  同时, 歧语言覆盖了大部分编程语言所必须的核心概念,
  在语法设计上与多种流行的成熟编程语言比较相似,
  方便在学习歧语言之后再去学习主流的编程语言。

+ **嵌入式脚本**:

  适合作为一个更大的软件的一部分, 作为扩展/插件而存在。

  歧语言可以编译成 WebAssembly (wasm), 很方便集成到一个软件中运行。
  由于多种编程语言都能编译成 wasm, 使用 wasm 作为扩展/插件机制的软件,
  将获得很高的灵活性, 歧语言只是可以选择使用的编程语言之一。

### 2.4 后端

歧语言设计上计划了这些后端:

+ **月兔后端**:

  歧语言代码通过歧语言编译器编译成月兔 (moonbit) 代码,
  再通过月兔编译器编译成 WebAssembly (wasm 二进制)。

  这是歧语言的主要后端, 大部分时候都使用这个后端。

+ **LLVM 后端**:

  歧语言代码通过歧语言编译器编译成 `LLVM IR`,
  再通过 LLVM 编译成机器指令 (二进制代码, 比如 RISC-V)。

  这个后端不用于编译到 wasm, 用于一些特殊用途。

### 2.5 主要设计原则

+ **简单**:

  尽量简化整个语言的设计。
  为了简单可以牺牲一部分语言的功能特性。

  比如为了简化文本编码的处理, 所有源代码文件统一使用 `utf-8` 编码。
  如果支持多种文本编码,
  实际使用中就会遇到需要手动选择哪种文本编码的问题, 或者遭遇乱码。
  支持多种文本编码带来的好处相比这种麻烦来说, 不值一提。

  代码语法尽量使用直观的设计。
  比如所有循环语句都使用 `循环` 开头, 定义一个变量的关键词是 `定义变量`。

+ **简短**:

  能用一个字的, 绝不用两个字。
  比如分支关键词使用 `若` 而不是 `如果`。

  此原则不能与 `简单` 原则冲突 (简单更优先)。
  减少字数不能显著增加阅读理解的难度。

+ 代码优先为 **阅读** 而优化:

  阅读代码的时间远多于写代码的时间, 所以语法设计首先为阅读而优化。
  其次再考虑输入。
  也就是输入代码稍微麻烦一点, 是可以接受的。

+ 代码优先为 **双拼** 输入法而优化:

  双拼输入法 (比如 `自然码`) 是推荐使用的中文输入法。

+ 考虑听觉使用:

  为了让此编程语言适用于更多的人, 如果无法通过视觉阅读代码 (视觉障碍),
  此时就要考虑使用纯听觉阅读代码。

  设计上应该尽量为这种情况提供便利, 比如尽量避免同音字。


## 3 参考 python

对 python 的设计的参考, 可能是对代码风格影响最大的一个决定。

### 3.1 注释和缩进

歧语言的注释和 python 基本一样, 仅支持单行注释 (为了简单),
从 `#` 字符开始至行尾都是注释。

类似 python, 歧语言强制使用缩进来区分代码块。
与使用大括号 `{` `}` 相比, 使用缩进会让代码看起来很整洁,
并且不同人写的代码风格基本一致 (没有太多选择)。

与 python 不同 (python 使用 4 个空格), 歧语言的每级缩进必须使用 2 个空格。
这纯粹是因为懒得多敲 2 次空格, 减缓了键盘的磨损。

许多编程语言, 特别是经典的比如 C 语言, 使用 `;` (分号) 作为一个语句的结束。
而 python 选择了使用换行区分语句, 也就是直接不需要分号。

歧语言不推荐把一行代码写的太复杂, 所以歧语言一行代码最多只能写一个语句。
一部分歧语言语句以 `。` (句号) 结束, 这是为了一个梗:

> "区分一个人是不是程序员, 就看一句话最后写的是句号还是分号"

好了, 现在程序员写的也是句号了 (狗头)

### 3.2 定义

python 定义函数的语法类似这样:

```python
def a():
    print(666)
```

对应的歧语言代码是:

```
定义a:
  输出 666。
```

关键词 `定义` 就来自于 python 的 `def`, 并且读起来更好听。

除了定义函数, 歧语言中的各种定义基本上都使用关键词定义, 比如:
`定义变量`, `定义常量`, `定义类型`, `定义枚举` 等。

变量在代码中很常用, 如果每次都写:

```
定义变量测试, 666。
```

就会很麻烦, 所以歧语言引入了一种语法糖, 使用关键词 `设`:

```
设测试 = 666
```


## 4 参考 rust

窝非常喜欢 rust 编程语言, 歧语言编译器也是用 rust 写的。
所以歧语言设计的很多方面都参考了 rust。

### 4.1 内置数据类型

内置数据类型可分为简单类型和复合类型, 简单类型又可分为自动类型和精确类型。

歧语言的精确数字类型的命名与 rust 基本一致, 比如:
`u8`, `u16`, `u32`, `u64`, `u128`,
`i8`, `i16`, `i32`, `i64`, `i128`,
`f32`, `f64`

歧语言增加了一种数字类型 `u1`, 别名 `二`, 只有 0 或 1 这两个取值。
按照 rust 的命名规则, `u1` 翻译过来是 "无符号 1 位二进制整数",
其是就是单个比特 (bit)。
这东西其实就相当于别的编程语言的布尔 (bool / boolean) 数据类型,
只是窝觉得 0 / 1 比 true, false, 真, 假, 阴, 阳, 之类的要更简单。
毕竟这东西本质上就只是需要 2 个取值 (所以别名叫 `二`),
而具体如何命名是无关紧要的 (比如叫 `天` 和 `地` 也不影响正常使用)。

除了上述的精确数字类型, 歧语言也提供了一些自动数字类型:
`整数`, `实数`, `数字`。
自动数字类型就是编译器来自动选择具体的数据类型,
程序员无需考虑, 从而简化了写代码。

内置数据类型 `文本` 类似于 rust 的 `String` 和 `char`,
也就是相当于 `utf-8` 字符串。
歧语言计划对 Unicode 提供很好的支持。

### 4.2 自动类型推导

rust 也是静态类型语言, 每个变量都具有确定的数据类型。
但是为了简化写代码, 不需要在每个地方都手动标注数据类型,
rust 引入了自动类型推导。
编译器可以判断一些数据类型, 省去了手动标注。

rust 的规则是, 每个函数的参数和返回值的数据类型必须手动标注,
在函数的内部使用自动类型推导。
歧语言的规则与 rust 基本一致。

### 4.3 表达式和语句

rust 的大部分东西都是表达式, 也就是有返回值。
比如 rust 的分支 (`if`), 模式匹配 (`match`) 就是表达式。
rust 函数的最后一个表达式的值自动返回。

歧语言的设计与 rust 类似, 歧语言的分支也是表达式,
歧语言的函数的最后一个表达式的值也自动返回。

歧语言明确区分表达式和语句,
典型的比如以句号 (`。`) 结尾的一定是语句,
还有赋值语句, 循环语句等。

### 4.4 没有类和继承

许多面向对象 (object) 的编程语言, 有类 (class) 和继承 (extend, inherit, implement, prototype) 等。
但是类和继承会让代码过于复杂。

rust 之中就没有类和继承, 只有结构体 (struct)。

歧语言的设计与 rust 类似, 没有类和继承, 只有 `定义类型` (struct)。

### 4.5 枚举和模式匹配

rust 具有很强大的枚举 (`enum`) 和模式匹配 (`match`),
rust 的枚举可以附带数据。

歧语言的设计与 rust 类似 (`定义枚举`)。


## 5 参考月兔 (moonbit)

月兔是一种很优秀的国产编程语言, 更契合中文编程的理念。

月兔在语言设计上很多地方类似 rust, 并且月兔实现了很多很重要的语言特性,
比如自动内存管理 (GC), 闭包 (closure) 等。
月兔有非常强大好用的编译器和工具链, 月兔支持编译成 WebAssembly (wasm)。

编译成月兔代码, 实际就是站在巨人的肩膀上。
歧语言的很多目标和设计, 可以直接借助月兔来实现,
这可以大大简化歧语言编译器的实现。

对于一种新的编程语言, 生态是最大的困难。
编译成月兔代码, 可以和月兔具有很好的互操作能力 (代码互相调用),
从而可以借助月兔的生态, 一定程度上缓解生态的问题。

此外, 月兔也有一些有趣的新设计。

### 5.1 方法函数

对于大部分编程语言来说, 某种数据类型 (类) 的方法函数 (method),
一般是和数据类型的定义写在一起的。
月兔的方法函数把数据类型和函数分开了。

月兔的函数, 如果第一个参数命名为 `self`, 就形成了一个方法函数,
可以使用调用方法的语法来调用, 比如:

```
func output(self: Int) {
  println(self)
}

func init {
  2.output()
}
```

歧语言也有类似的设计, 并且更进一步, 只要函数的第一个参数的数据类型对的上,
就可以作为方法函数, 而不限制第一个参数的名称, 比如:

```
定义类型吃货:
  名字(文本)。

# 此处是方法函数
定义吃, 参数自己(吃货)、东西(文本):
  输出 "\(自己的名字)吃了\(东西)"。

定义变量窝, 吃货。
窝的名字是 "喵喵"。

定义变量饭, "汤面条"。

# 调用方法函数
窝吃饭。  # 喵喵吃了汤面条
```

### 5.2 字符串插值

字符串插值本身并不是新鲜的东西, 比如 js 的字符串也有这个语法:

```js
let a = 1;
`===${a}---`
```

但是月兔的写法比较有趣 (使用反斜杠和小括号):

```
"x = \(x)"
```

歧语言使用了基本相同的设计:

```
输出 "\(自己的名字)吃了\(东西)"。
```

另外, 歧语言增加了直接插入 Unicode 编码值 (16 进制, Unicode Scalar Value) 的语法,
比如:

```
"\[3001, 3002]"
```


## 6 参考 js

JavaScript (ECMA script) 是一种使用非常广泛, 生态非常强大的编程语言。
也是窝喜欢的编程语言之一。

### 6.1 模块与导入导出

歧语言的模块与导入导出设计类似 js:
每个源文件 (`.歧`) 是一个模块 (module),
使用 `导入`, `导出` 语句, 使用相对路径来导入别的模块。

歧语言内置的标准库叫做 `标准库`,
每个歧语言代码的最上面都有一条隐藏的默认导入语句:

```
导入标准库的序章的全部。
```

### 6.2 顶级语句

有些编程语言, 要求代码都要写在函数里, 才能执行, 比如月兔。

js 支持顶级语句, 也就是不必写在函数里。
歧语言使用了相同的设计, 比如:

```
输出 666。
```

### 6.3 闭包

闭包 (closure) 是一种非常强大的语言功能。
一个函数在创建时, 会捕捉周围环境中的变量,
典型使用场景是一个函数返回另一个函数。

歧语言的闭包功能与 js 基本一致。

### 6.4 自动数据类型

歧语言有这些自动数字类型: `整数`, `实数`, `数字`

其中 `整数` 包括 *有符号整数* 和 *无符号整数*, 对应的精确数据类型可能是:
`u8`, `u16`, `u32`, `u64`, `u128`,
`i8`, `i16`, `i32`, `i64`, `i128`

`实数` 其实就是 IEEE-754 浮点数, 对应 `f32`, `f64`

`数字` 就是同时包含了整数和浮点数, 类似 js 的 `Number` 数据类型。

各种精确数据类型有时候很有用, 是不可缺少的。
但是对于正在入门的编程语言初学者来说, 这么一大堆各种数据类型就特别不友好,
很可能还没开始写代码就被绕晕了 (窝当年就被坑过)。

一开始使用自动数据类型, 可以大大简化写代码 (无脑全部使用 `数字`), 方便入门学习。
以后如果遇到更高级的应用, 必须使用精确数据类型时, 再回来考虑也不迟。

自动数据类型, 就是编译器来自动选择具体的精确数据类型。
有编译器默认值, 不同的编译选项也可能会影响编译器的决定。
另外, 自动数据类型也相当于某种程度的泛型。


## 7 中文语法

歧语言最大的特色就是, 这是一种中文编程语言。

在上面各方面参考了各大现有的编程语言之后, 终于轮到中文语法设计了。

### 7.1 无空格语法 (自动分词)

大部分现有的编程语言都是英文编程语言, 也就是基于英文来设计的。
和英文相比, 中文有一个非常显著的典型特征, 那就是没有空格。
英文的各个单词之间使用空格分隔, 所以英文分词几乎就不是个问题。
相反, 中文的自然语言处理 (NLP) 需要使用复杂的 AI 模型来处理分词。

比如歧语言代码:

```
窝吃饭。
```

对应英文风格的编程语言是:

```
窝.吃(饭);
```

无空格的语法设计更接近中文的使用习惯, 读起来更舒服。

但是在编译器里面塞一个 AI 模型不是个好主意。
首先 AI 模型非常复杂, 会大大增加编译器的复杂度, 同时大大降低编译速度。
其次, AI 的精确度永远不可能达到 100%,
就算是 99% 的优秀 AI, 那剩下的 1% 怎么办 ?
能够想象 1% 代码随机编译失败的编译器嘛 ?

歧语言使用了一种简单的方法, **当前词表**。
也就是正在处理的代码对应的上下文中, 所有定义和导入的有意义的词 (标识符)。

比如在编译 `窝吃饭` 这个代码的时候, 歧语言编译器的当前词表可能是这样的:

| 标识符 | 类型 | 定义位置 |
| :----- | :--- | -------: |
| 吃货 | 类型 | 第 1 行 |
| 吃 | 函数 | 第 4 行 |
| 窝 | 变量 | 第 8 行 |
| 饭 | 变量 | 第 11 行 |

根据这个词表进行分词, 那么只有一种结果: `窝 吃 饭`

### 7.2 主谓宾语法

现代汉语的大部分句子符合语法 "主谓宾",
还有变形 "主谓" (没有宾), "动宾" (没有主)。

所以歧语言的特色语法设计就是 "主谓宾" 语法,
比如 `窝吃饭`, 对应英文风格的编程语言 `主.谓(宾)` 的语法。

除了主谓宾, 现代汉语句子中的语法成分还有 "定状补",
但是这些难以设计成编程语言语法, 就没有采用。

### 7.3 歧义处理 (分词歧义)

中文分词之所以难, 就是存在大量的分词歧义。

在写歧语言代码的时候, 很可能也会遇到分词歧义。
比如在上面 `窝吃饭` 的栗子中, 如果同时再定义一个函数 `吃饭`,
那么就有 2 种分词结果: `窝 吃 饭` 和 `窝 吃饭`

不要怕 !
如果遇到歧义, 歧语言编译器会使用一种非常简单粗暴的方式来处理:
直接编译错误 !
把困难留给写代码的人类。 (就是这么任性)

这也正是 "无歧义中文编程语言" 中 "无歧义" 的意思:
歧语言编译器会保证没有歧义 (狗头)

不过, 歧语言也提供了一些手动处理歧义的语法,
用来处理特殊场景, 比如通过添加空格手动分词。


## 8 总结

每个编程爱好者都有一个梦想, 那就是创造一种自己的编程语言。 (不是)

但是, 现有的编程语言已经至少好几百种了, 还有必要再多一种嘛 ?

迷茫着, 等待着, 终于, 遇到了一个机会:

当年, 文言文编程语言 (<https://github.com/wenyan-lang/wenyan>)
横空出世, 宛如一道刺破黑暗的强光。

突然就意识到, 对哦, 怎么不试试中文呢 ?
在这片还没怎么被开发的蓝海, 或许还潜藏着很多宝物。

如果把:

```
窝吃饭。
```

变成代码, 嗯, 那一定很有趣。

### 8.1 对编程语言的主观偏见

窝承认, 窝对各种编程语言具有强烈的主观偏见。
也就是喜欢某些编程语言, 讨厌某些编程语言,
喜欢某些语言设计和功能特性, 讨厌某些设计。

毫无意外, 这些偏见都被带入了此编程语言的设计之中。

但是另一方面, 在设计上总是要做出选择和取舍的。 没有最佳答案。
所以窝就任性的根据自己的主观偏见进行了选择。

中文编程领域, 整体来看还处于发展的早期阶段, 距离成熟和主流还有很远。
百花齐放是好事, 期待出现更多的中文编程语言 !

以上。

----

许可 (LICENSE): `CC-BY-SA 4.0`
